﻿#include "PluginSpec.h"
#include "PluginSpec_p.h"
#include <QCoreApplication>
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QRegularExpression>
#include "StringUtils.h"

namespace CVV
{

namespace Infrastructure
{

namespace ExtensionSystem
{

using namespace  ExtensionSystem::Internal;
PluginSpec::PluginSpec():d(new PluginSpecPrivate(this))
{
}

PluginSpec::~PluginSpec()
{
    delete d;
    d = nullptr;
}

PluginSpec::State PluginSpec::state() const
{
    return d->m_state;
}
bool PluginSpec::hasError() const
{
    return d->m_hasError;
}

QString PluginSpec::errorString() const
{
    return d->m_errorString;
}
/**
 * @brief 属性信息
 * @return
 */
QString PluginSpec::name() const
{
    return d->m_name;
}

void PluginSpec::setName(const QString &name)
{
    d->m_name = name;
}

QString PluginSpec::version() const
{
    return d->m_version;
}

void PluginSpec::setVersion(const QString &version)
{
    d->m_version = version;
}

QString PluginSpec::compatVersion() const
{
    return d->m_compatVersion;
}

void PluginSpec::setCompatVersion(const QString &compatVersion)
{
    d->m_compatVersion = compatVersion;
}

QString PluginSpec::description() const
{
    return d->m_description;
}

void PluginSpec::setDescription(const QString &description)
{
    d->m_description = description;
}

QString PluginSpec::vendor() const
{
    return d->m_vendor;
}

void PluginSpec::setVendor(const QString &vendor)
{
    d->m_vendor = vendor;
}

QString PluginSpec::copyright() const
{
    return d->m_copyright;
}

void PluginSpec::setCopyright(const QString &copyright)
{
    d->m_copyright = copyright;
}

QString PluginSpec::license() const
{
    return d->m_license;
}

void PluginSpec::setLicense(const QString &license)
{
    d->m_license = license;
}

QString PluginSpec::category() const
{
    return d->m_category;
}

void PluginSpec::setCategory(const QString &category)
{
    d->m_category = category;
}

QString PluginSpec::categoryIcon() const
{
    return d->m_categoryIcon;
}

void PluginSpec::setCategoryIcon(const QString &icon)
{
    d->m_categoryIcon = icon;
}

QString PluginSpec::revision() const
{
    return d->m_revision;
}

void PluginSpec::setRevision(const QString &revision)
{
    d->m_revision = revision;
}

QString PluginSpec::url() const
{
    return d->m_url;
}

void PluginSpec::setUrl(const QString &url)
{
    d->m_url = url;
}

QJsonArray PluginSpec::params() const
{
    return d->m_params;
}

void PluginSpec::setParams(const QJsonArray &params)
{
    d->m_params=params;
}

QJsonArray PluginSpec::tempParams() const
{
    return  d->m_tempParams;
}

void PluginSpec::setTempParams(const QJsonArray &tempParams)
{
    d->m_tempParams=tempParams;
}

QJsonArray PluginSpec::outParams() const
{
    return  d->m_outParams;
}

void PluginSpec::setOutParams(const QJsonArray &outParams)
{
    d->m_outParams=outParams;
}

QJsonArray PluginSpec::inputParams() const
{
    return  d->m_inputParams;
}

void PluginSpec::setInputParams(const QJsonArray &inputParams)
{
     d->m_inputParams=inputParams;
}

QString PluginSpec::code() const
{
    return d->m_code;
}

void PluginSpec::setCode(const QString &code)
{
    d->m_code = code;
}
QString PluginSpec::iconPath() const
{
    return d->m_iconPath;
}

void PluginSpec::setIconPath(const QString &iconPath)
{
    d->m_iconPath = iconPath;
}

PluginSpec *PluginSpec::read(const QString &filePath)
{
    auto spec = new PluginSpec;
    if (!spec->d->read(filePath)) { // not a Qt Creator plugin
        delete spec;
        return nullptr;
    }
    return spec;
}

/**
 * 插件私有类开始
 */
namespace {
    const char PLUGIN_METADATA[] = "MetaData";
    const char PLUGIN_NAME[] = "Name";
    const char PLUGIN_VERSION[] = "Version";
    const char PLUGIN_COMPATVERSION[] = "CompatVersion";
    const char VENDOR[] = "Vendor";
    const char COPYRIGHT[] = "Copyright";
    const char LICENSE[] = "License";
    const char DESCRIPTION[] = "Description";
    const char URL[] = "Url";
    const char CATEGORY[] = "Category";
    const char CATEGORYICON[] = "CategoryIcon";
    const char PARAMS[] = "Params";
    const char INPUTPARAMS[] = "InputParams";
    const char OUTPARAMS[] = "OutParams";
    const char TEMPPARAMS[] = "TempParams";
    const char CODE[]="Code";
    const char ICONPATH[]="IconPath";
}
static inline QString msgValueMissing(const char *key)
{
  return QCoreApplication::translate("PluginSpec", "\"%1\" is missing").arg(QLatin1String(key));
}
static inline QString msgValueIsNotAString(const char *key)
{
    return QCoreApplication::translate("PluginSpec", "Value for key \"%1\" is not a string")
            .arg(QLatin1String(key));
}


static inline QString msgValueIsNotAMultilineString(const char *key)
{
    return QCoreApplication::translate("PluginSpec", "Value for key \"%1\" is not a string and not an array of strings")
            .arg(QLatin1String(key));
}

static inline QString msgInvalidFormat(const char *key, const QString &content)
{
    return QCoreApplication::translate("PluginSpec", "Value \"%2\" for key \"%1\" has invalid format")
            .arg(QLatin1String(key), content);
}

PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec)
    : m_pluginSpec(spec)
{

}

PluginSpecPrivate::~PluginSpecPrivate()
{

}
bool PluginSpecPrivate::isValidVersion(const QString &version)
{
    QRegularExpression reg("^([0-9]+)(?:[.]([0-9]+))?(?:[.]([0-9]+))?(?:_([0-9]+))?$");
    return reg.match(version).hasMatch();
}


/**
 * @brief PluginSpecPrivate::read
 * 1.读取插件库信息
 * @param fileName
 * 文件名
 * @return
 */
bool PluginSpecPrivate::read(const QString &fileName)
{
    qDebug() << "\nReading meta data of" << fileName;
    m_name
        = m_version
        = m_compatVersion
        = m_vendor
        = m_copyright
        = m_license
        = m_description
        = m_url
        = m_category
        = m_categoryIcon
        = QString();

    m_state = PluginSpec::Invalid;
    m_hasError = false;
    m_errorString.clear();
    m_metaData = QJsonObject();
    QFileInfo fileInfo(fileName);
    m_filePath = fileInfo.absoluteFilePath();
    m_loader.setFileName(m_filePath);
    if (m_loader.fileName().isEmpty()) {
        qDebug() << "Cannot open file";
        return false;
    }

    QJsonObject metaObj = m_loader.metaData();
    if (!readMetaData(metaObj))
        return false;
    m_state = PluginSpec::Read;
    return true;
}
inline bool readMultiLineString(const QJsonValue &value, QString *out)
{
    if (value.isString()) {
        *out = value.toString();
    } else if (value.isArray()) {
        QJsonArray array = value.toArray();
        QStringList lines;
        for (const QJsonValue &v : array) {
            if (!v.isString())
                return false;
            lines.append(v.toString());
        }
        *out = lines.join(QLatin1Char('\n'));
    } else {
        return false;
    }
    return true;
}
bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
{
   // qDebug() << "MetaData:" << QJsonDocument(pluginMetaData).toJson();
    QJsonValue value;
    value = pluginMetaData.value(QLatin1String("IID"));
    if (!value.isString()) {
       qDebug() << "Not a plugin (no string IID found)";
        return false;
    }
    //if (value.toString() != PluginManager::pluginIID()) {
    //   qDebug() << "Plugin ignored (IID does not match)";
    //    return false;
    //}

    value = pluginMetaData.value(QLatin1String(PLUGIN_METADATA));
    if (!value.isObject())
        return reportError(tr("Plugin meta data not found"));
    m_metaData = value.toObject();

    value = m_metaData.value(PLUGIN_NAME);
    if (value.isUndefined())
        return reportError(msgValueMissing(PLUGIN_NAME));
    if (!value.isString())
        return reportError(msgValueIsNotAString(PLUGIN_NAME));
     m_name = value.toString();

     value = m_metaData.value(QLatin1String(CODE));
     if (value.isUndefined())
         return reportError(msgValueMissing(CODE));
     if (!value.isString())
         return reportError(msgValueIsNotAString(CODE));
     m_code = value.toString();

    value = m_metaData.value(QLatin1String(PLUGIN_VERSION));
    if (value.isUndefined())
        return reportError(msgValueMissing(PLUGIN_VERSION));
    if (!value.isString())
        return reportError(msgValueIsNotAString(PLUGIN_VERSION));
     m_version = value.toString();

    if (!isValidVersion(m_version))
        return reportError(msgInvalidFormat(PLUGIN_VERSION, m_version));

    //参数
    value = m_metaData.value(PARAMS);
    if(!value.isUndefined()&&!value.isArray())
        return reportError(msgValueMissing(PLUGIN_VERSION));
    m_params = value.toArray();


    //输入参数
    value = m_metaData.value(INPUTPARAMS);
    if(!value.isUndefined()&&!value.isArray())
        return reportError(msgValueMissing(PLUGIN_VERSION));
    m_inputParams = value.toArray();

    //输出参数
    value = m_metaData.value(OUTPARAMS);
    if(!value.isUndefined()&&!value.isArray())
        return reportError(msgValueMissing(PLUGIN_VERSION));
    m_outParams = value.toArray();

    //临时参数
    value = m_metaData.value(TEMPPARAMS);
    if(!value.isUndefined()&&!value.isArray())
        return reportError(msgValueMissing(PLUGIN_VERSION));
    m_tempParams = value.toArray();


    value = m_metaData.value(QLatin1String(PLUGIN_COMPATVERSION));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(PLUGIN_COMPATVERSION));
     m_compatVersion = value.toString(m_version);
    if (!value.isUndefined() && !isValidVersion(m_compatVersion))
        return reportError(msgInvalidFormat(PLUGIN_COMPATVERSION, m_compatVersion));

    value = m_metaData.value(QLatin1String(VENDOR));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(VENDOR));
     m_vendor = value.toString();

     value = m_metaData.value(QLatin1String(ICONPATH));
     if (!value.isUndefined() && !value.isString())
         return reportError(msgValueIsNotAString(ICONPATH));
     m_iconPath = value.toString();

    value = m_metaData.value(QLatin1String(COPYRIGHT));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(COPYRIGHT));
    m_copyright = value.toString();

    value = m_metaData.value(QLatin1String(DESCRIPTION));
    if (!value.isUndefined() && !readMultiLineString(value, &m_description))
        return reportError(msgValueIsNotAString(DESCRIPTION));

    value = m_metaData.value(QLatin1String(URL));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(URL));
    m_url = value.toString();

    value = m_metaData.value(QLatin1String(CATEGORY));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(CATEGORY));
    m_category = value.toString();

    value = m_metaData.value(QLatin1String(CATEGORYICON));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(CATEGORYICON));
    m_categoryIcon = value.toString();

    value = m_metaData.value(QLatin1String(LICENSE));
    if (!value.isUndefined() && !readMultiLineString(value, &m_license))
        return reportError(msgValueIsNotAMultilineString(LICENSE));

    return true;
}


bool PluginSpecPrivate::reportError(const QString &err)
{
    m_errorString = err;
    m_hasError = true;
    return true;
}

/**
 * @brief PluginSpecPrivate::loadLibrary
 * 2.加载插件库
 * @return
 */
bool PluginSpecPrivate::loadLibrary()
{
    if (m_hasError)
        return false;
    /*if (m_state != PluginSpec::Resolved) {
        if (m_state == PluginSpec::Loaded)
            return true;
        m_errorString = QCoreApplication::translate("PluginSpec", "Loading the library failed because state != Resolved");
        m_hasError = true;
        return false;
    }*/
    if (!m_loader.load()) {
        m_hasError = true;
//        qDebug() << "111=" << m_loader.errorString();
        m_errorString = QDir::toNativeSeparators(m_filePath)
            + QString::fromLatin1(": ") + m_loader.errorString();
        qDebug() << "111=" << m_errorString;
        return false;
    }
    auto *pluginObject = qobject_cast<IPlugin*>(m_loader.instance());
    if (!pluginObject) {
        m_hasError = true;
        m_errorString = QCoreApplication::translate("PluginSpec", "Plugin is not valid (does not derive from IPlugin)");
        m_loader.unload();
        return false;
    }
    m_state = PluginSpec::Loaded;
    m_plugin = pluginObject;
    m_plugin->pluginSpec = m_pluginSpec;
    return true;
}
/**
 * @brief PluginSpecPrivate::initializePlugin
 * 3.初始化插件
 * @return
 */
bool PluginSpecPrivate::initializePlugin()
{
    if (m_hasError)
        return false;
    if (m_state != PluginSpec::Loaded) {
        if (m_state == PluginSpec::Initialized)
            return true;
        m_errorString = QCoreApplication::translate("PluginSpec", "Initializing the plugin failed because state != Loaded");
        m_hasError = true;
        return false;
    }
    if (!m_plugin) {
        m_errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to initialize");
        m_hasError = true;
        return false;
    }
    QString err;
    if (!m_plugin->initialize(m_arguments, &err)) {
        m_errorString = QCoreApplication::translate("PluginSpec", "Plugin initialization failed: %1").arg(err);
        m_hasError = true;
        return false;
    }
    m_state = PluginSpec::Initialized;
    return true;
}

/**
 * @brief PluginSpecPrivate::kill
 * delete 插件
 */
void PluginSpecPrivate::kill()
{
    if (!m_plugin)
        return;
    delete m_plugin;
    m_plugin = nullptr;
    m_state = PluginSpec::Deleted;
}

}

}

}



